package com.kehutong.repair.service.applet;

import java.sql.Timestamp;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import org.coraframework.inject.Inject;
import org.coraframework.json.JSONArray;
import org.coraframework.json.JSONObject;
import org.coraframework.logger.Logger;
import org.coraframework.logger.LoggerFactory;
import org.coraframework.mvc.http.Bean;
import org.coraframework.mvc.http.ReqMapping;
import org.coraframework.mvc.http.result.Result;
import org.coraframework.mvc.webservice.WebService;
import org.coraframework.orm.Pool;
import org.coraframework.orm.jdbc.JdbcSession;
import org.coraframework.threadpool.ScheduledExecutor;
import org.coraframework.threadpool.ThreadPool;
import org.coraframework.util.EnumUtil;
import org.coraframework.util.Objects;
import org.coraframework.util.time.TimeUtil;

import com.kehutong.common.DoveClient;
import com.kehutong.common.util.Token;
import com.kehutong.repair.entity.Remark;
import com.kehutong.repair.entity.Repairs;
import com.kehutong.repair.entity.RepairsType;
import com.kehutong.repair.entity.RollOutDetail;
import com.kehutong.repair.enums.OperationType;
import com.kehutong.repair.enums.RepairsStatus;
import com.kehutong.repair.enums.Source;
import com.kehutong.repair.enums.Target;
import com.kehutong.repair.enums.TimeUnit;
import com.kehutong.repair.utils.SendMessageUtils;

@WebService("/repairs/applet/service")
public class AppletRepairsService {

    protected final Logger logger = LoggerFactory.getLogger(AppletRepairsService.class);

    @Inject
    private DoveClient doveClient;

    @Inject
    private JdbcSession jdbcSession;

    @Inject
    private SendMessageUtils sendMessageUtils;

    @Inject
    private ThreadPool threadPool;

    @ReqMapping("/get")
    public Object get(JSONObject jsonObject){
        return Pool.get(Repairs.class,jsonObject.getString("id"));
    }

//    @ReqMapping("/getSubscribe")
//    public Object getSubscribe(Token session){
//        return Result.success();
//    }

    @Bean(newInstance = true, copy = true)
    @ReqMapping("/save")
    public Object save(Token token, Repairs item, JSONObject jsonObject) throws Exception {
        setProperty(token, item, jsonObject);
        jdbcSession.insert(item);

        RepairsType repairsType = Pool.get(RepairsType.class, item.getTypeId());
        timeOutConfig(item, repairsType, token.getCompanyNo());

        StringBuilder title = new StringBuilder();
        title.append("您有新的").append(item.getType().getDesc()).append(",请及时处理！");
        sendCard(item, title.toString(),null, token.getCompanyNo());
        insertDetail(item, "提交");

//        if (item.getSource() == Source.APP) { //todo 代报修
//            return Result.success(200);
//        }
//        if (item.getSource() == Source.WECHAT_OFFICIAL_ACCOUNT) { //TODO 公众号
//            return Result.success(getResultJSONObiect(session));
//        }
        return Result.success();
    }

    @ReqMapping("/update")
    public Object update(Token token, JSONObject jsonObject) throws Exception {
        Repairs root = Pool.get(Repairs.class, jsonObject.getString("id"));
        root.setRepairsStatus(RepairsStatus.CANCEL);
        jdbcSession.updateById(root);

        StringBuilder title = new StringBuilder();
        title.append("您的").append(root.getType().getDesc()).append("已取消！");
        sendCard(root, title.toString(), null, token.getCompanyNo());
        insertDetail(root,"取消");

        return Result.success();
    }

    @ReqMapping("/reminder")
    public Object reminder(Token token, JSONObject jsonObject) throws Exception {
        Repairs root = Pool.get(Repairs.class,jsonObject.getString("id"));

        StringBuilder title = new StringBuilder();
        title.append("您有新的").append(root.getType().getDesc()).append(",请及时处理！");
        sendCard(root, title.toString(), null, token.getCompanyNo());
        return Result.success();
    }

    @ReqMapping("/evaluation")
    public Object evaluation(Token token, JSONObject jsonObject) throws Exception {
        Repairs root = Pool.get(Repairs.class,jsonObject.getString("id"));
        root.setTotal(jsonObject.getInteger("total"));
        root.setAttitude(jsonObject.getInteger("attitude"));
        root.setSpeed(jsonObject.getInteger("speed"));
        root.setEvaluation(jsonObject.getString("evaluation"));
        root.setRepairsStatus(RepairsStatus.COMPLETE);
        jdbcSession.updateById(root);

        StringBuilder title = new StringBuilder();
        title.append("您处理的").append(root.getType().getDesc()).append("订单,有新的评价信息！");
        sendCard(root, title.toString(), null, token.getCompanyNo());

        RollOutDetail detail = Pool.newInstance(RollOutDetail.class);
        detail.setOldEmployeeNo(token.getUuid());
        detail.setOldEmployeeName(token.getUsername());
        detail.setRemark("评价");
        detail.setRepairsId(root.getId());
        jdbcSession.insert(detail);

        return Result.success();
    }

    @ReqMapping("/list")
    public Object list(Token token, JSONObject jsonObject) {
        jsonObject.put("createById", token.getUuid());

        String search = jsonObject.getString("search");
        List<Repairs> repairs;
        if (Objects.isEmpty(search)) {
            repairs = jdbcSession.findArray(Repairs.class)
                    .eq(jsonObject, "createById", "repairsStatus", "type")
                    .eq("deleted", false)
                    .order("createTime", "desc")
                    .exe();
        } else {
            List<RepairsType> types = jdbcSession.findArray(RepairsType.class)
                    .eq("name", search).exe();

            List<String> ids = new ArrayList<>();
            for (RepairsType type : types) {
                ids.add(type.getId());
            }

            if (Objects.nonEmpty(ids)) {
                repairs = jdbcSession.findArray(Repairs.class)
                        .markBegin()
                        .eq(jsonObject, "createById", "type").and()
                        .in("typeId", ids)
                        .eq(jsonObject, "repairsStatus")
                        .markEnd()
                        .eq("deleted", false)
                        .order("createTime", "desc")
                        .exe();
            } else {
                repairs = jdbcSession.findArray(Repairs.class)
                        .markBegin()
                        .like("noId", search).or()
                        .like("detail", search)
                        .markEnd()
                        .eq(jsonObject, "createById", "repairsStatus", "type")
                        .eq("deleted", false)
                        .order("createTime", "desc")
                        .exe();
            }
        }
        return repairs;
    }

    @ReqMapping("/remark")
    public Object remark(Token token, String remark, String id) throws Exception {
        Repairs repairs = Pool.get(Repairs.class, id);
        Remark item = Pool.newInstance(Remark.class);
        item.setOrderId(id);
        item.setContent(remark);
        item.setRemarkName(repairs.getCreateBy());
        item.setRemarkNameId(repairs.getCreateById());
        jdbcSession.insert(item);
        // 记录操作
        insertDetail(repairs,"备注了");

        // 发送企微消息模板
        List<String> employeeNos = repairs.getAttnEmployeeNo();
        if (Objects.equal(repairs.getRepairsStatus(), RepairsStatus.PENDING)) {
            employeeNos.addAll(repairs.getExeEmployeeNo());
        }
        String title = "您的" + repairs.getType().getDesc() + "工单追加了一条记录！";
        if(Objects.nonEmpty(employeeNos)) {
            JSONObject resObj = doveClient.post("/basic/employee/service/list", (http) -> {
                http.addHeader("companyNo", token.getCompanyNo());
                JSONObject params = new JSONObject();
                params.put("id", employeeNos);
                http.setBody(params.toString());
            });
            JSONArray employees = resObj.getJSONArray("data");
            sendCard(repairs, title, employees, token.getCompanyNo());
        }
        return Result.success();
    }

    @ReqMapping("/getRemark")
    public Object getRemark(String id){
        return jdbcSession.findArray(Remark.class).eq("orderId", id)
                .order("createTime", "desc").exe();
    }

    /**
     * 发送企业微信消息模板
     *
     * @param item
     * @param title
     * @param employess
     * @param companyNo
     * @throws Exception
     */
    private void sendCard(Repairs item, String title, JSONArray employess, String companyNo) throws Exception {
        RepairsType repairsType = Pool.get(RepairsType.class,item.getTypeId());

        if(Objects.isEmpty(employess)){
            JSONObject obj = doveClient.post("/flow/service/role/get/employee/ByDepartmentChildren", (http) -> {
                http.addHeader("companyNo", companyNo);
                http.addParam("communityNo", item.getCommunityNo());
                http.addParam("roleId", repairsType.getExeRoleId());
            });
            employess = obj.getJSONArray("data");

            JSONObject resObj = doveClient.post("/flow/service/role/get/employee/ByDepartmentChildren", (http) -> {
                http.addHeader("companyNo", companyNo);
                http.addParam("communityNo", item.getCommunityNo());
                http.addParam("roleId", repairsType.getAttnRoleId());
            });
            employess.addAll(resObj.getJSONArray("data"));
        }

        if(Objects.nonEmpty(employess)) {
            List<String> empIds = new ArrayList<>();
            employess.forEach(obj -> {
                JSONObject jsonObject = (JSONObject) obj;
                empIds.add(jsonObject.getString("id"));
            });

            threadPool.execute(() -> {
                try {
                    sendMessageUtils.sendEnterpriseWeChatMessageTemplate(item, title, String.join("|", empIds), companyNo);
                } catch (Exception e) {
                    e.printStackTrace();
                    logger.error("发送企微模板消息出错: {}", e);
                }
            });
        }
    }

    private void setProperty(Token token, Repairs item,JSONObject jsonObject) throws Exception {
        item.setSource(EnumUtil.getEnum(Source.class, jsonObject.getString("source")));
//        if (item.getSource() == Source.APP) { //TODO 代报修
//            Employee employee = Pool.get(Employee.class, session.getUserId());
//            item.setContactName(employee.getName());
//            item.setContactMobile(employee.getMobile());
//        }
        item.setCreateById(token.getUuid());
        item.setCreateBy(token.getUsername());
        item.setNoId(getNoId());
        item.setType(EnumUtil.getEnum(OperationType.class, jsonObject.getString("type")));
        item.setRepairsStatus(RepairsStatus.PENDING);
        String startTime = jsonObject.getString("startTime");
        String endTime = jsonObject.getString("endTime");
        if(Objects.nonEmpty(startTime) && Objects.nonEmpty(endTime)){
            DateTimeFormatter fmt = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
            item.setAppointmentStartTime(LocalDateTime.parse(startTime, fmt));
            item.setAppointmentEndTime(LocalDateTime.parse(endTime, fmt));
        }else{
            item.setAppointmentStartTime(LocalDateTime.now());
            item.setAppointmentEndTime(LocalDateTime.now());
        }
        item.setImageIds(Arrays.asList(jsonObject.getString("imageIds").split(",")));

        RepairsType type = Pool.get(RepairsType.class, item.getTypeId());

        LocalDateTime timeOut = item.getAppointmentEndTime();
        switch (type.getUnit()){
            case MINUTE:
                timeOut = timeOut.plusMinutes(type.getTime());
                break;
            case HOUR:
                timeOut = timeOut.plusHours(type.getTime());
                break;
            case DAY:
                timeOut = timeOut.plusDays(type.getTime());
                break;
            case WEEK:
                timeOut = timeOut.plusWeeks(type.getTime());
                break;
            case MONTH:
                timeOut = timeOut.plusMonths(type.getTime());
                break;
            default:
                break;
        }
        item.setTimeOut(timeOut);

        JSONObject obj = doveClient.post("/flow/service/role/get/employee/ByDepartmentChildren", (http) -> {
            http.addHeader("companyNo", token.getCompanyNo());
            http.addParam("communityNo", item.getCommunityNo());
            http.addParam("roleId", type.getExeRoleId());
        });
        JSONArray exeEmployees = obj.getJSONArray("data");
        List<String> exeList = new ArrayList<>();
        exeEmployees.forEach(object->{
            exeList.add(((JSONObject) object).getString("id"));
        });
        item.setExeEmployeeNo(exeList);

        JSONObject attnRoleObj = doveClient.post("/flow/service/role/get/employee/ByDepartmentChildren", (http) -> {
            http.addHeader("companyNo", token.getCompanyNo());
            http.addParam("communityNo", item.getCommunityNo());
            http.addParam("roleId", type.getAttnRoleId());
        });
        JSONArray attnEmployees = attnRoleObj.getJSONArray("data");
        List<String> attnList = new ArrayList<>();
        attnEmployees.forEach(object->{
            attnList.add(((JSONObject) object).getString("id"));
        });
        item.setAttnEmployeeNo(attnList);
    }

    private String getNoId(){
        int count = jdbcSession.findArray(Repairs.class).exe().size() + 1;
        String id = String.valueOf(count);
        while(id.length()< 6){
            id = "0" + id;
        }
        id = "1" + id;
        return id;
    }

    private static final long getDelayMillis(String time) {
        Timestamp timeStamp;
        if (time.length() == 19) {
            timeStamp = Timestamp.valueOf(time);
        } else {
            throw new IllegalArgumentException("argument must be [yyyy-MM-dd HH:mm:ss]");
        }
        while (timeStamp.getTime() < TimeUtil.timeMillis()) {
            timeStamp.setTime(timeStamp.getTime() + TimeUtil.MILLIS_PER_DAY);
        }
        return (timeStamp.getTime() - TimeUtil.timeMillis());
    }

    /**
     * 插入详情备注
     *
     * @param root
     * @param action
     */
    private void insertDetail(Repairs root,String action){
        RollOutDetail detail = Pool.newInstance(RollOutDetail.class);
        detail.setOldEmployeeName(root.getCreateBy());
        detail.setOldEmployeeNo(root.getCreateById());
        detail.setRemark(action);
        detail.setRepairsId(root.getId());
        jdbcSession.insert(detail);
    }

    /**
     * 超时配置
     * @param repairs
     * @param repairsType
     * @return
     */
    private void timeOutConfig(Repairs repairs, RepairsType repairsType, String companyNo) throws Exception {
        LocalDateTime appointmentEndTime = repairs.getAppointmentEndTime();
        int time = repairsType.getTime();

        appointmentEndTime = setTime(time, repairsType.getUnit(), appointmentEndTime);

        JSONObject object = doveClient.post("/flow/service/rule/list/all", (http) -> {
            http.addHeader("companyNo", companyNo);
            JSONObject params = new JSONObject();
            params.put("type", 4);
            params.put("name", repairs.getType().getDesc());
            http.setBody(params.toString());
        });
        JSONArray flowRules;
        if (object.getInteger("code") == 200) {
            flowRules = object.getJSONArray("data");
        } else {
            logger.error("{}", object.getString("message"));
            return;
        }

        if (Objects.isEmpty(flowRules) || Objects.isEmpty(((JSONObject)flowRules.get(0)).getJSONArray("nodeList"))) {
            logger.error("超时配置：{}", "为配置督导流程");
            return;
        }

        JSONObject flowRule = (JSONObject)flowRules.get(0);
        JSONArray nodeList = flowRule.getJSONArray("nodeList");
        for (int i = 0; i < nodeList.size(); i++) {

            int temp = i;
            JSONObject resultFlowRuleNode = doveClient.post("/flow/service/rule/get/flowRuleNode", (http) -> {
                http.addHeader("companyNo", companyNo);
                http.addParam("id", nodeList.get(temp));
            });
            JSONObject flowRuleNode = resultFlowRuleNode.getJSONObject("data");
            int waitTime = flowRuleNode.getInteger("waitTime");
            appointmentEndTime = setTime(waitTime, EnumUtil.getEnum(TimeUnit.class, flowRuleNode.getJSONObject("unit").getString("id")), appointmentEndTime);
            String format = appointmentEndTime.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
            logger.debug("appointmentEndTime: {}", format);
            ScheduledExecutor.schedule(new Runnable() {
                @Override
                public void run() {
                    logger.info("超时配置提醒！");
                    Repairs newRepairs = Pool.get(Repairs.class, repairs.getId());
                    if(Objects.equal(RepairsStatus.PENDING, newRepairs.getRepairsStatus()) || Objects.equal(RepairsStatus.PROCESSING, newRepairs.getRepairsStatus())){
                        try {
                            String title = "超时" + flowRuleNode.getJSONObject("action").getString("desc") + "！";
                            JSONArray employees;
                            String targetNo = flowRuleNode.getString("targetNo");
                            if (flowRuleNode.getJSONObject("target").getString("id") == Target.IDENTITY.getId()) {
                                JSONObject attnRoleObj = doveClient.post("/flow/service/role/get/employee/ByDepartmentChildren", (http) -> {
                                    http.addHeader("companyNo", companyNo);
                                    http.addParam("communityNo", newRepairs.getCommunityNo());
                                    http.addParam("roleId", targetNo);
                                });
                                employees = attnRoleObj.getJSONArray("data");
                            }else {
                                JSONObject resultDepartment = doveClient.post("/basic/department/service/get", (http) -> {
                                    http.addHeader("companyNo", companyNo);
                                    http.addParam("id", targetNo);
                                });
                                JSONObject department = resultDepartment.getJSONObject("data");
                                String pid = department.getString("id");
                                JSONObject resultDepartmentList = doveClient.post("/basic/department/service/list", (http) -> {
                                    http.addHeader("companyNo", companyNo);
                                    JSONObject params = new JSONObject();
                                    params.put("parent_id", pid);
                                    http.setBody(params.toString());
                                });
                                JSONArray departmentList = resultDepartmentList.getJSONArray("data");

                                Set<String> collect = new HashSet<>();
                                if (departmentList.size() != 0) {
                                    departmentList.forEach(obj -> {
                                        JSONObject departmentJSONObject = (JSONObject) obj;
                                        collect.add(departmentJSONObject.getString("id"));
                                    });
                                }
                                collect.add(pid);

                                JSONArray employeeList = new JSONArray();
                                collect.forEach(item -> {
                                    JSONObject employeesJSONObject = new JSONObject();
                                    try {
                                        employeesJSONObject = doveClient.post("/basic/employee/service/list", (http) -> {
                                            http.addHeader("companyNo", companyNo);
                                            JSONObject params = new JSONObject();
                                            params.put("departments", item);
                                            http.setBody(params.toString());
                                        });
                                    } catch (Exception e) {
                                        e.printStackTrace();
                                        logger.error("employeesJSONObject报错: {}", e);
                                    }
                                    employeeList.addAll(employeesJSONObject.getJSONArray("data"));
                                });
                                employees = employeeList;
                            }

                            sendCard(newRepairs, title, employees, companyNo);


                        } catch (Exception e) {
                            logger.error("异步定时任务（模板消息）报错：{}", e);
                        }
                    }
                }
            }, getDelayMillis(format));
        }
    }

    /**
     * 设置超时时间
     * @param time
     * @param unit
     * @param appointmentTime
     * @return
     */
    private LocalDateTime setTime(int time, TimeUnit unit, LocalDateTime appointmentTime){
        switch (unit) {
            case MINUTE:
                return appointmentTime.plusMinutes(time);
            case HOUR:
                return appointmentTime.plusHours(time);
            case DAY:
                return appointmentTime.plusDays(time);
            case WEEK:
                return appointmentTime.plusWeeks(time);
            case MONTH:
                return appointmentTime.plusMonths(time);
            default: throw new IllegalArgumentException("time unit error!!!");
        }

    }
}
